﻿using System;
using System.Collections.Generic;
using Pulsus;
using SlamCms.Common;
using SlamCms.Data;
using SlamCms.Data.Repositories;
using SlamCms.Web;

namespace SlamCms.Tracking
{
	public class TrackingService : ITrackingService
	{
		private readonly SlamCmsContext _slamCmsContext;
		private readonly ICookieManager _cookieManager;
		private readonly Lazy<ITrackingRepository> _trackingRepository;

		public TrackingService(SlamCmsContext slamCmsContext, Lazy<ITrackingRepository> trackingRepository)
		{
			_slamCmsContext = slamCmsContext;
			_cookieManager = slamCmsContext.HttpContext.GetCookieManager();
			_trackingRepository = trackingRepository;
		}

		public void TrackCurrentPage()
		{
			try
			{
				var what = GetWhat();
				var trackingType = GetTrackingType();
				var contentItemId = GetContentItemId();
				TrackPageInternal(what, trackingType, contentItemId);
				_cookieManager.Remove(Constants.Cookies.TrackingType, false, false);
			}
			catch (Exception ex)
			{
				// Ignore exceptions, just log them
				_slamCmsContext.LogManager.CreateEvent()
										 .AddException(ex)
										 .Text("There was a problem tracking current page")
										 .Level(LoggingEventLevel.Warning)
										 .Push();
			}
		}

		public void TrackPage(string what, string how, string contentItemId)
		{
			try
			{
				TrackPageInternal(what, how, contentItemId);
			}
			catch (Exception ex)
			{
				// Ignore exceptions, just log them
				_slamCmsContext.LogManager.CreateEvent()
										 .AddException(ex)
										 .Text("There was a problem tracking page")
										 .Level(LoggingEventLevel.Warning)
										 .Push();
				
			}
		}

		public void TrackNotFound(string url)
		{
			try
			{
				TrackNotFoundInternal(url);
			}
			catch (Exception ex)
			{
				// Ignore exceptions, just log them
				_slamCmsContext.LogManager.CreateEvent()
											 .AddException(ex)
											 .Level(LoggingEventLevel.Warning)
											 .Text("There was a problem tracking not found")
											 .Push();
				
			}
		}

		public void TrackTagHits(string[] tagIdentifiers)
		{
			try
			{
				var visitId = GetTrackVisitId();
				int? userId = _slamCmsContext.User != null ? _slamCmsContext.User.UserId : (int?)null;

				var trackTagHits = new List<TrackTagHit>();

				foreach (var tagIdentifier in tagIdentifiers)
					trackTagHits.Add(TrackTagHit.Create(visitId, userId, tagIdentifier));

				_trackingRepository.Value.Save(trackTagHits);
			}
			catch (Exception ex)
			{
				// Ignore exceptions, just log them
				_slamCmsContext.LogManager.CreateEvent()
										 .AddException(ex)
										 .Level(LoggingEventLevel.Warning)
										 .Text("There was a problem tracking tag hits")
										 .Push();
			}
		}

		protected void TrackPageInternal(string what, string how, string contentItemId)
		{
			var visitId = GetTrackVisitId();
			int? userId = _slamCmsContext.User != null ? _slamCmsContext.User.UserId : (int?)null;
			var referrer = GetReferrer();

			what = what.Truncate(1000);
			referrer = referrer.Truncate(1000);
			how = how.Truncate(1000);

			var trackPage = Data.TrackPage.Create(visitId, userId, what, how, referrer, contentItemId);

			_trackingRepository.Value.Save(new[] { trackPage });

			SetTrackVisitId(visitId);
		}

		protected void TrackNotFoundInternal(string url)
		{
			int? userId = _slamCmsContext.User != null ? _slamCmsContext.User.UserId : (int?)null;
			var referrer = GetReferrer();

			url = url.Truncate(1000);
			referrer = referrer.Truncate(1000);

			var trackNotFound = Data.TrackNotFound.Create(userId, url, referrer);

			_trackingRepository.Value.Save(new[] { trackNotFound });
		}

		protected Guid GetTrackVisitId()
		{
			Guid visitId;

			if (Guid.TryParse(_cookieManager.Get(Constants.Cookies.TrackVisitId), out visitId))
				return visitId;

			return Guid.NewGuid();
		}

		protected string GetTrackingType()
		{
			var trackingType = _cookieManager.Get(Constants.Cookies.TrackingType);
			return trackingType;
		}

		protected void SetTrackVisitId(Guid trackVisitId)
		{
			_cookieManager.Set(Constants.Cookies.TrackVisitId, trackVisitId.ToString());
		}

		protected string GetReferrer()
		{
			string referrer = null;

			if (_slamCmsContext.HttpContext.Request.UrlReferrer != null)
			{
				referrer = _slamCmsContext.HttpContext.Request.UrlReferrer.PathAndQuery;

				if (this.IsExternalReferrer())
					referrer = _slamCmsContext.HttpContext.Request.UrlReferrer.AbsoluteUri;
			}

			return referrer;
		}

		protected string GetWhat()
		{
			return _slamCmsContext.HttpContext.Request.Url.PathAndQuery;
		}

		protected string GetContentItemId()
		{
			return _slamCmsContext.AdministrationBar.ContentItem == null ? string.Empty : _slamCmsContext.AdministrationBar.ContentItem.ContentItemId;
		}

		protected bool IsExternalReferrer()
		{
			if (_slamCmsContext.HttpContext.Request.UrlReferrer != null && (_slamCmsContext.HttpContext.Request.UrlReferrer.Host != _slamCmsContext.HttpContext.Request.Url.Host || !_slamCmsContext.HttpContext.Request.UrlReferrer.PathAndQuery.Contains(_slamCmsContext.HttpContext.Request.ApplicationPath)))
				return true;
			return false;
		}
	}
}